#include "queue.h"

void Queue_Init(Queue_Handle_t * hqueue, QueueDataType_t * buffer, uint16_t len);
void Queue_Clear(Queue_Handle_t * hqueue);
uint16_t Queue_Count(Queue_Handle_t * hqueue);
Queue_StatusType_t Queue_Push(Queue_Handle_t * hqueue, QueueDataType_t data);
uint16_t Queue_Push_Array(Queue_Handle_t * hqueue, QueueDataType_t * pdatas, uint16_t len);
Queue_StatusType_t Queue_Pop(Queue_Handle_t * hqueue, QueueDataType_t * pdata);
uint16_t Queue_Pop_Array(Queue_Handle_t * hqueue, QueueDataType_t * pdatas, uint16_t len);
Queue_StatusType_t Queue_Peek(Queue_Handle_t * hqueue, QueueDataType_t * pdata);
uint16_t Queue_Peek_Array(Queue_Handle_t * hqueue, QueueDataType_t * pdatas, uint16_t len);

/**
 * @brief 初始化（创建）队列，每个队列必须先执行该函数才能使用
 * 
 * @param hqueue 
 * @param buffer 
 * @param len 
 */
void Queue_Init(Queue_Handle_t * hqueue, QueueDataType_t * buffer, uint16_t len)
{
    hqueue->buffer = buffer;
    hqueue->buffer_length = len;
    Queue_Clear(hqueue);
}

/**
 * @brief 清空队列（设置到初始化状态）
 * 
 * @param hqueue 
 */
void Queue_Clear(Queue_Handle_t * hqueue)
{
    hqueue->head = 0;
    hqueue->tail = 0;
}

/**
 * @brief 获取队列内数据的个数
 * 
 * @param hqueue 
 * @return uint16_t 
 */
uint16_t Queue_Count(Queue_Handle_t * hqueue)
{
    if(hqueue->head <= hqueue->tail)
    {
        return (uint16_t)(hqueue->tail - hqueue->head);
    }
    else
    {
        return (uint16_t)(hqueue->buffer_length + hqueue->tail - hqueue->head);
    }
}

/**
 * @brief 单元素入队
 * 
 * @param hqueue 
 * @param data 
 * @return Queue_StatusType_t 
 */
Queue_StatusType_t Queue_Push(Queue_Handle_t * hqueue, QueueDataType_t data)
{
    uint16_t tmp = (hqueue->tail + 1) % hqueue->buffer_length;

    if(tmp == hqueue->head)
    {
        return QUEUE_OVERLOAD;
    }
    else
    {
        hqueue->buffer[hqueue->tail] = data;
        hqueue->tail = tmp;
        if (hqueue->Watermark < Queue_Count(hqueue))
        {
            hqueue->Watermark = Queue_Count(hqueue);
        }

        return QUEUE_OK;
    }
}

/**
 * @brief 多元素入队
 * 
 * @param hqueue 
 * @param pdatas 
 * @param len 
 * @return uint16_t 
 */
uint16_t Queue_Push_Array(Queue_Handle_t * hqueue, QueueDataType_t * pdatas, uint16_t len)
{
    uint16_t i;
    for(i=0; i<len; i++)
    {
        if(Queue_Push(hqueue,pdatas[i]) == QUEUE_OVERLOAD)
        {
            break;
        }
    }
    
    uint16_t qLen = Queue_Count(hqueue);
    if (hqueue->Watermark < qLen)
    {
        hqueue->Watermark = qLen;
    }
    return i;
}

/**
 * @brief 出队一个元素
 * 
 * @param hqueue 
 * @param pdata 
 * @return Queue_StatusType_t 
 */
Queue_StatusType_t Queue_Pop(Queue_Handle_t * hqueue, QueueDataType_t * pdata)
{
    if(hqueue->head == hqueue->tail)
    {
        return QUEUE_VOID;
    }
    else
    {
        *pdata = hqueue->buffer[hqueue->head];
        hqueue->head = (hqueue->head + 1) % hqueue->buffer_length;
        return QUEUE_OK;
    }
}

/**
 * @brief 多元素出队
 * 
 * @param hqueue 
 * @param pdatas 
 * @param len 
 * @return uint16_t 
 */
uint16_t Queue_Pop_Array(Queue_Handle_t * hqueue, QueueDataType_t * pdatas, uint16_t len)
{
    uint16_t i;
    for(i=0; i<len; i++)
    {
        if(Queue_Pop(hqueue, &pdatas[i]) == QUEUE_VOID)
        {
            break;
        }
    }
    return i;
}

/**
 * @brief 窥探队列头部一个元素数据（不出队元素）
 * 
 * @param hqueue 
 * @param pdata 
 * @return Queue_StatusType_t 
 */
Queue_StatusType_t Queue_Peek(Queue_Handle_t * hqueue, QueueDataType_t * pdata)
{
    if(hqueue->head == hqueue->tail)
    {
        return QUEUE_VOID;
    }
    else
    {
        *pdata = hqueue->buffer[hqueue->head];
        return QUEUE_OK;
    }
}

/**
 * @brief 窥探队列头部多个元素数据（不出队元素）
 * 
 * @param hqueue 
 * @param pdatas 
 * @param len 
 * @return uint16_t 
 */
uint16_t Queue_Peek_Array(Queue_Handle_t * hqueue, QueueDataType_t * pdatas, uint16_t len)
{
    uint16_t i;
    if(hqueue->head == hqueue->tail)
    {
        return 0;
    }
    if(Queue_Count(hqueue) < len)
    {
        len = Queue_Count(hqueue);
    }
    for(i=0; i<len; i++)
    {
        pdatas[i] = hqueue->buffer[(hqueue->head + i) % hqueue->buffer_length];
    }
    return len;
}